#include <string.h>
#include "bitmap.h"
#include "error.h"

bitmap::bitmap():   bpp(0),width(0),height(0),data(NULL) {}
bitmap::bitmap(unsigned int wid,unsigned int hei,unsigned char bp):
bpp( bp ),width( wid ),height( hei )
{    data = new unsigned char[wid*height*bp/8]; }
bitmap::bitmap(char* filename, unsigned char bp){
    bpp = bp;
    loadFile(filename);
}
bitmap::~bitmap(){
    if( data ) delete[] data;
}

unsigned int    bitmap::getWidth()  {return width;}
unsigned int    bitmap::getHeight() {return height;}
unsigned int    bitmap::getBpp()    {return bpp;}
unsigned char*  bitmap::Data()      {return data;}

static void bmp_pixel_swap(unsigned char* pos){
    unsigned char stack;
    stack = *pos;
    *pos = *(pos+2);
    *(pos+2) = stack;
}

bool    bitmap::loadFile(char* filename){
    //error::log(error::con_only,"Loading BMP file %s...\n",filename);

    file infile;
    if( !infile.open(filename) ){
        error::log(error::log_err,"File %s not found",filename);
        return false;
    }

    //error::log(error::con_only,"Fetching BMP data...");
    bmp_header hdr;
    bmp_info info;
    infile.read(&hdr,sizeof(bmp_header),1);
    if( hdr.bmp_str[0] != 'B' || hdr.bmp_str[1] != 'M' ){
        error::log(error::log_err,"%s's magic number not corresponding to windows bitmap, aborting...",filename);
        return false;
    }
    infile.read(&info,sizeof(bmp_info),1);
    if( info.bmp_comp_meth != 0 ){
        error::log(error::log_err,"the driver only supports uncompressed bmp (here:%d)",info.bmp_comp_meth);
        return false;
    }
    if( info.bmp_bpp > bpp ){
        bpp = info.bmp_bpp;
    }

    width = info.bmp_width;
    height = info.bmp_height;

    //error::log(error::con_only,"Done\n");

    unsigned char targetsize = 0 ;
    unsigned char destsize = 0 ;
    switch(info.bmp_bpp){
        default:    error::log(error::log_err,"the driver doesn't support this pixel format(here:%d)",info.bmp_bpp);
                    return false;
        case 24:
            targetsize = sizeof(b24px);
            break;
        case 8:
            targetsize = sizeof(b8px);
            if( bpp < 24 ) bpp = 24;
            break;
    }

    switch(bpp){
        case 24:
            destsize = sizeof(b24px);
            break;
        case 32:
            destsize = sizeof(b32px);
            break;
    }

    unsigned int octPerRow = info.bmp_width*targetsize;
    unsigned int pitch = octPerRow / 4;
    pitch*=4;
    if( pitch < octPerRow ) pitch += 4;

    if( data ) delete[] data;
    data = new unsigned char[info.bmp_width*info.bmp_height*destsize];
    unsigned char datarow[pitch];

    if( targetsize == sizeof(b24px) || targetsize == sizeof(b32px) ){
        for( unsigned int j = 0; j < info.bmp_height; j++ ){
            infile.read(datarow,pitch,1);
            unsigned int lineoffset = (info.bmp_height - j - 1) * info.bmp_width;
            for( unsigned int i = 0; i < info.bmp_width; i++ ){
                unsigned int offset = (i + lineoffset)*destsize;
                memcpy( data + offset, datarow + i * targetsize , targetsize );
                bmp_pixel_swap( data + offset );
            }
        }
    }
    else if( targetsize == sizeof(b8px) ){
        b32px* palette = NULL;
        palette = new b32px[256];
        infile.read( palette, 256, sizeof(b32px) );
        for( unsigned int j = 0; j < info.bmp_height; j++ ){
            infile.read(datarow,pitch,1);
            unsigned int lineoffset = (info.bmp_height - j - 1) * info.bmp_width;
            for( unsigned int i = 0; i < info.bmp_width; i++ ){
                unsigned int offset = (i + lineoffset)*destsize;
                b24px* stackpx = (b24px*)(data + offset);
                b32px stacksrc = palette[*(datarow + i * targetsize)];
                stackpx->u0 = stacksrc.u16;
                stackpx->u8 = stacksrc.u8;
                stackpx->u16 = stacksrc.u0;
            }
        }
    }

    return true;
}

void    bitmap::loadSFile(file* infile,unsigned int wid,unsigned int hei,unsigned short in_bpp){
    //error::log(error::con_only,"Reading BMP from filestream...\n");
    if( !infile->opened() ){
        error::log(error::log_err,"Stream not initialized");
        return;
    }
    if( in_bpp > bpp ){
        bpp = in_bpp;
    }

    width = wid;
    height = hei;

    unsigned char targetsize = 0 ;
    unsigned char destsize = 0 ;
    switch(in_bpp){
        default:    error::log(error::log_err,"the driver doesn't support this pixel format(here:%d)",in_bpp);
                    return;
        case 24:
            targetsize = sizeof(b24px);
            break;
    }
    switch(bpp){
        case 24:
            destsize = sizeof(b24px);
            break;
        case 32:
            destsize = sizeof(b32px);
            break;
    }

    if( data ) delete[] data;
    data = new unsigned char[wid*hei*destsize];
    unsigned char datarow[wid*targetsize];

    for( unsigned int j = 0; j < hei; j++ ){
        infile->read(datarow,targetsize,wid);
        for( unsigned int i = 0; i < wid; i++ ){
            memcpy( data + ( i + j*wid )*destsize, datarow + i * targetsize, targetsize);
        }
    }
}
